package org.loong.crypto.core.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509ExtensionUtils;
import org.bouncycastle.cert.bc.BcX509ExtensionUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.loong.crypto.common.exception.CryptoException;

public final class KeyTools {

    /**
     * Create the subject key identifier.
     * 
     * @param publicKey the publicKey
     * @return SubjectKeyIdentifer ASN.1 structure
     * @throws CryptoException the {@link CryptoException}
     */
    public static SubjectKeyIdentifier createSubjectKeyId(final PublicKey publicKey) throws CryptoException {
        try {
            final ASN1Sequence keyASN1Sequence;
            try (final ASN1InputStream pubKeyAsn1InputStream = new ASN1InputStream(new ByteArrayInputStream(publicKey.getEncoded()));) {
                final Object keyObject = pubKeyAsn1InputStream.readObject();
                if (keyObject instanceof ASN1Sequence) {
                    keyASN1Sequence = (ASN1Sequence) keyObject;
                } else {
                    // PublicKey key that doesn't encode to a ASN1Sequence. Fix this by creating a BC object instead.
                    final PublicKey altKey = (PublicKey) KeyFactory.getInstance(publicKey.getAlgorithm(), BouncyCastleProvider.PROVIDER_NAME).translateKey(publicKey);
                    try (final ASN1InputStream altKeyAsn1InputStream = new ASN1InputStream(new ByteArrayInputStream(altKey.getEncoded()))) {
                        keyASN1Sequence = (ASN1Sequence) altKeyAsn1InputStream.readObject();
                    }
                }
                X509ExtensionUtils x509ExtensionUtils = new BcX509ExtensionUtils();
                return x509ExtensionUtils.createSubjectKeyIdentifier(SubjectPublicKeyInfo.getInstance(keyASN1Sequence));
            }
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new CryptoException(e.getMessage(), e);
        }
    }
}
